package com.hunttown.common.sharding.client.router;

import com.hunttown.common.sharding.client.RouteFactor;
import com.hunttown.common.sharding.client.config.ShardConfig;
import com.hunttown.common.sharding.client.config.TableConfig;
import com.hunttown.common.sharding.client.exception.RouteException;
import com.hunttown.common.sharding.client.support.RouteResult;
import com.hunttown.common.sharding.client.util.BeanPropertyAccessUtil;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class SimpleRouter implements Router<RouteFactor> {
    private final Log log = LogFactory.getLog(SimpleRouter.class);
    private ShardConfig shardConfig;

    public SimpleRouter() {
    }

    public void setShardConfig(ShardConfig shardConfig) {
        this.shardConfig = shardConfig;
    }

    public RouteResult doRoute(RouteFactor routeCondition) throws RouteException {
        if (routeCondition == null) {
            throw new IllegalArgumentException("routeCondition or its logicalTableName cann't be null or empty!");
        } else {
            RouteResult result = new RouteResult();

            try {
                String logicalTableName = routeCondition.getLogicalTableName();
                TableConfig tableConfig = this.getTableConfig(logicalTableName);
                if (tableConfig != null) {
                    Object routeField = this.getRouteField(tableConfig, routeCondition);
                    if (routeField != null) {
                        int dbIndex = tableConfig.getRouteRule().getDbIndex(routeField);
                        result.addDbIdentities(tableConfig.getDbIndices()[dbIndex]);
                        int tableIndex = tableConfig.getRouteRule().getTableIndex(routeField);
                        result.setPhysicalTableName(tableConfig.getTableIndices()[tableIndex]);
                        this.log.info("表[" + logicalTableName + "]分表键为[" + routeField + "]的记录映射到数据库[" + tableConfig.getDbIndices()[dbIndex] + "]的表[" + tableConfig.getTableIndices()[tableIndex] + "].");
                        String tableNameField = tableConfig.getTabNameField();
                        if (StringUtils.isNotBlank(tableNameField)) {
                            this.rewriteTableName(routeCondition.getParameter(), tableNameField, result.getPhysicalTableName());
                            this.rewriteDataSourceName(routeCondition.getParameter(), "dataSourceName", tableConfig.getDbIndices()[dbIndex]);
                        }
                    }
                }

                return result;
            } catch (Throwable var9) {
                throw new RouteException("Route error!", var9);
            }
        }
    }

    private Object getRouteField(TableConfig tableConfig, RouteFactor routeFactor) throws RouteException {
        Object routeField = routeFactor.getRouteField("shard_field");
        if (routeField == null) {
            String fieldName = tableConfig.getRouteField();
            Object para = routeFactor.getParameter();
            if (StringUtils.isNotBlank(fieldName)) {
                if (para instanceof Map) {
                    Map<Object, Object> paramMap = (Map) para;
                    Object routeObj = paramMap.get(fieldName);
                    if (routeObj != null) {
                        routeField = routeObj;
                    } else if (paramMap.size() == 1) {
                        Entry entry;
                        for (Iterator i$ = paramMap.entrySet().iterator(); i$.hasNext(); routeObj = entry.getValue()) {
                            entry = (Entry) i$.next();
                        }

                        try {
                            routeField = BeanPropertyAccessUtil.getPropertyValue(fieldName, routeObj);
                        } catch (Exception var11) {
                            throw new RouteException("getRouteField error!", var11);
                        }
                    }
                } else if (para != null) {
                    try {
                        routeField = BeanPropertyAccessUtil.getPropertyValue(fieldName, para);
                    } catch (Exception var10) {
                        throw new RouteException("getRouteField error!", var10);
                    }
                }
            }
        }

        return routeField == null ? routeFactor.getParameter() : routeField;
    }

    private TableConfig getTableConfig(String logicalTableName) {
        TableConfig tableConfig = null;
        Map<String, TableConfig> tableConfigMap = this.shardConfig.getTableConfig();
        if (StringUtils.isBlank(logicalTableName)) {
            Entry entry;
            if (tableConfigMap.size() == 1) {
                for (Iterator i$ = tableConfigMap.entrySet().iterator(); i$.hasNext(); tableConfig = (TableConfig) entry.getValue()) {
                    entry = (Entry) i$.next();
                }
            }
        } else {
            tableConfig = (TableConfig) tableConfigMap.get(logicalTableName);
        }

        return tableConfig;
    }

    private void rewriteTableName(Object parameterObj, String tableNameField, String physicalTableName) throws RouteException {
        try {
            if (parameterObj instanceof Map) {
                ((Map) parameterObj).put(tableNameField, physicalTableName);
            } else if (parameterObj != null) {
                BeanPropertyAccessUtil.setPropertyValue(tableNameField, physicalTableName, parameterObj);
            }

        } catch (Exception var5) {
            throw new RouteException("rewrite table name error!", var5);
        }
    }

    private void rewriteDataSourceName(Object parameterObj, String dataSourceField, String dataSourceName) throws RouteException {
        try {
            if (parameterObj instanceof Map) {
                ((Map) parameterObj).put(dataSourceField, dataSourceName);
            } else if (parameterObj != null) {
                BeanPropertyAccessUtil.setPropertyValue(dataSourceField, dataSourceName, parameterObj);
            }

        } catch (Exception var5) {
            throw new RouteException("rewrite data source name error!", var5);
        }
    }

    public Map<String, List<String>> getRouteInfo(String logicTableName) {
        Map<String, List<String>> routeInfoMap = null;
        TableConfig tableConfig = this.getTableConfig(logicTableName);
        if (tableConfig != null) {
            String[] dbs = tableConfig.getDbIndices();
            String[] tables = tableConfig.getTableIndices();
            int tablePerDb = tables.length / dbs.length;
            routeInfoMap = new HashMap();
            List<String> tablesInDb = null;

            for (int i = 0; i < tables.length; ++i) {
                int step = i / tablePerDb;
                if (!routeInfoMap.containsKey(dbs[step]) || routeInfoMap.get(dbs[step]) == null) {
                    tablesInDb = new ArrayList();
                    routeInfoMap.put(dbs[step], tablesInDb);
                }

                tablesInDb.add(tables[i]);
            }
        }

        return routeInfoMap;
    }
}